{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 数据处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 安装类库\n",
    "# !mkdir /home/aistudio/external-libraries\n",
    "# !pip install imgaug -t /home/aistudio/external-libraries\n",
    "import sys\n",
    "sys.path.append('/home/aistudio/external-libraries')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "image shape: (32, 32, 3)\n",
      "label value: horse\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADFCAYAAAARxr1AAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAHC1JREFUeJztnXuMXHd1x79nZu689732erO24zixnZg8XGEgFNTybAOqFKhaBH9U+SMCKoFUBP9EVGqp1EpUKqD+UVEFNSKVKIEWUFIaKCGiStNAHiTEeZgkdmJnba937X3NzO687+kfMw47+z17Pdldr3fN+UjW7h7fufd378yZe89bVBWO49jELvcCHGcz4wriOBG4gjhOBK4gjhOBK4jjROAK4jgRuII4TgSuII4TwZoURERuE5GXROSYiNy1XotynM2CrDaSLiJxAC8D+CCAUwCeBPAJVX1xpdfEg4QGqaBDlslkaLtYTEhWqVRIptowjxOG/Pp4LE6yIJkgWT7XY+2RJLXFKskaTd5OY/b1jRtfTf2D20iWyeR4NSHvs1xeNGQLJEun+XpjhY9Ao9kkWRAEJEum0vYO6DB8IDWurSrLwpBlANA0rnmjXiNZPN75/k9PnkepUOQPyjL4E9I9bwdwTFVfBQARuQ/A7QBWVJAgFWDnjXs7ZDfd9BbaLpNJkuyV40dJVq/NmseplPj1OeODv2v3MMnecevvkUxCvuCnnjlOsvOzJZI1svxaAMjz5x5//PE/J9lbbnonycqLvM8jzz9FsueeY9nBA3y9LYUDgNm5Asm2je4k2dV795FMhL8B6k3+UqmDv/iqTVbshcWyucbSLMunz46TrL833/H3333+S+b+lrOWR6wxAEtXcqot60BEPiUiT4nIU806fyM5zmbmkhvpqnq3qh5W1cPxgB9zHGczs5ZHrNMAdi35e2dbtiIahqhXOm+JszPnaLvkju0kGx1h2bmpunmcuXN8i85m2V5ZWJwh2anTL5NsbIRujEinUyRTnSfZ4NCAuca3HtrPxxkdJVnTeJ6ePc/X7OWjL5FsboYfkRYW+HGqt99eY98wPwdWQ76O52fPkyyMZ0kmYtkLbDtVq/yoWq7aj6qLNd5nkOZ1yzL756LGR5u13EGeBLBPRK4RkSSAjwN4YA37c5xNx6rvIKraEJHPAvhvAHEA96jqC+u2MsfZBKzlEQuq+iCAB9dpLY6z6fBIuuNEsKY7yJslnohjaLivQ1avsh87YQTXioU5klWMeAAADA8MsWw4T7KbbmGfPowg5dnJUyTLBmykZ7MchGs07DWm0xyrSSX42DHl2EEixobywQPXkWz3VVeRLNfDwcggawVHgcoiOx1qjSLJ5ubZ2TGzwO+X5XAIhM9lscD7q4Z2iKBoxA8b81Mkyy6Lt1SNwLOF30EcJwJXEMeJwBXEcSJwBXGcCDbUSE+nUrju2j0dsiDJ6SepFMvm56dJ1qzb2bxqJBcOD/WT7PoDe0k2PcsJkM8+8wqvMdhBsr7+XpIVY2ysAnZUOQZedyrBstERjhTnMntI9sJznBXQk+HM2zDJDgcAiNVYnkuwsZxK8vdsocTnVyxwhoOEbCxXipwBsNiwsyZmjIzjsMjv4WK9MzrfWOGzsxy/gzhOBK4gjhOBK4jjROAK4jgRbKyRnk7hwP5rO2S5HEef6w023IoFjo42a3Z0dWqCX79jhKsHBw2jenqanQGJBDsNjII5hIYhGQ/s76Aw5G1j4KyCVJIj6VYFYCPNsljIr80aRvZ8jVPOAaA4z8ZyT9pwLoR8jqkGy3LCH7eZeY7MWxF3GE4NAIgpX8dymdPlK4VOw90qJzb339VWjvNbiiuI40TgCuI4EbiCOE4EriCOE8GavFgicgJAEUATQENVD19ke6SWNWtLpXgJVg+rgzccIFk6sJd/8jinGoyNctOHIMGvtxrMZY30jHiTv1uGhvpIFvbY3pJUiutBmk32vtSqRhO8uLFuw0OUCtjzk0/yeqbO83EB4PzZsyyrsmercJZ7dZQWjfUY1/HE+DGS7drDdSw92zlVCADiFfZ4FYxGIPW5ztqWZqO7VJP1cPO+V1W5rYXjXAH4I5bjRLBWBVEAPxGRX4rIp6wNlnZWXCjZASnH2ays9RHr3ap6WkS2A3hIRH6tqo8s3UBV7wZwNwCM7R71mdPOlmKtbX9Ot39OicgP0Gpo/chK28cTCfQNdqZ81BucXlGscPrBVbvYcOtL2c0GqsUTJBscYCMvFuMbaCbLxvPAIDd86AlZtnPn1SQLM7YxmO8zajCMdn+lBW6cEDcaSzRqnLJjpchUjQ6FzzzxS3ONP3/61ySrL/B7M33mdX5xjGtWtg9xus/4aTbSc3l+X7btto30nGFsx4x6oHBZR8hupxqs+hFLRHIi0nPhdwB/AOD51e7PcTYja7mDjAD4gYhc2M+/qeqP12VVjrNJWEvr0VcB3LKOa3GcTYe7eR0ngg2tB4kl4sgNDXbIzkycoO3OzXPccWgHt+iPiz36K2M0Ieg3WvxncmyQDxl1I8UFrqvIGEb60DAfI8jbl1gDNi6TaXY61OtsFFtdAWtlNtwLJa6VeOKJX5DsJw/afpXZOTZ2G4aRX2uywRsPeLuRPv4+Tjf5PTh/hiP4e27gJhkA0GvU2+SM0XoVGvV2iY10x/ltwBXEcSJwBXGcCFxBHCeCDTXSIYBkOtOtYxk2LhNGNHtukY1VNYxDAIgbowV6BzgSn+/laO9rpzhV/twkp4hnhQ3gA9ewkT02yvMNAWCxyeeTSPB6YtY45TpHyE+f4pEBD/2EDfJnn+Xo+My0nSOXTnH6ftMYV2AFpStlw7mwwGn1mRjPXS+e59eePn7GXOPYGI9zyKZ4PuJcrHOf1phqC7+DOE4EriCOE4EriONE4AriOBFsbCQ9rgjyncbtjt2DtF3/do5Sq5XWXLaN9FyMU6Mnpzmye+Qod2t87DFOSD53miP7KWXjcnGGL+d7/9AeLbDnwC6SBXHeJ2KcLTB+jlPgf/xfPyfZIz/jNPZa0xgjYJwLANTrvG29wVkFVaM8oVbj2vXZWXYu5AM+v1jduLbG+wcAsxk+djLJRnoq0/mZEqPUwcLvII4TgSuI40TgCuI4EbiCOE4EFzXSReQeAH8EYEpVb2zLBgF8B8AeACcAfExVOQS9jFAbqDQ6N0ulOXocpA0jK8HGbio0irgBLM5wJP34S9xM7MXnx0k2foKN0ECHSDY7x4byo2d/xdsVuOYeAMb2cvp2b5bPJybsiDh69FWSPf5/L5Bs0QiQJ1NsFDdWyEioGvMDq1WeM1iu8BzGWIJfO7c4SbIwxSMotvezAyMd8GcCAAaG2MlTNcZQ9IWd6e4Jo2mgRTd3kG8CuG2Z7C4AD6vqPgAPt/92nCuOiypIu43P8kSf2wHc2/79XgAfWed1Oc6mYLU2yIiqTrR/P4tWAweTpY3jinN8e3aczcyajXRtNRhasX5RVe9W1cOqerinn+0Nx9nMrDaSPikio6o6ISKjADgkbdBo1HFuqrMT+LZhTkNv1NlYLRr914KQI7MAcPQZnjOICjd1G+jlVPT5XjbSk0Y0e7ZiNG9juxTT5+xU8mdffJRkxblTJDO/wYzIdyLOqfZ9vbzuUDl1X41GawBQq7ODoW7Jmvxk8NZ33UiyXaOjJHv28SMkKzU5Cl+Hvcbd+/k9jBkTA0bLne/rI//5U3N/tK+utmIeAHBH+/c7ANy/yv04zqbmogoiIt8G8HMAB0TklIjcCeDLAD4oIq8A+ED7b8e54rjoI5aqfmKF/3r/Oq/FcTYdHkl3nAg2NN292WiiNNtpgMUavIRGg0eHzc5w5Lo4YxtuR37BUfMbr+URbHWjIdz0JDct6+/lZnK5PHdTLysb5PkcR3oBIDHN9dnlEn9fNRt8jrkcHzubZsM9k+GMgmKRzzkmfL0BIJ1mI79mOFD6hrk84a3vexvJ9h+4hmQDu7jZ3q+feY1khYadqCF5jppXxYjilzqj/U3tbgSb30EcJwJXEMeJwBXEcSJwBXGcCFxBHCeCjfVi1ZuYmez0JhSmOU2hYTRomJpkz1S5YKeaTIyzx6t6/imShVVOISsWjayZkNMzrtrOnq258+zFajT4tQDQ28OvX+znGpGFItdaiPG2NZrs7Yon2OMUGKMhEgl7jERvH3vB4lPcJGH3wb0ku/7wQZJl8vx+Hfr9m0m2fYzTj8oLdqJrWYzmEMZcx3OLndexERrNKwz8DuI4EbiCOE4EriCOE4EriONEsKFGepAMMDq2s0MWj/MSyiVOw0iBDbyzdTbQAKBhGHQnTj7N60kY8+16uYlAIsWGdt0YX1AqcR1KKrXfXOPeEa5PqRsNERp1TgOxjO+E0RwxNL7+8oOcFjKQ5RECAJBO8XGu2suG++9+6ADJ9ozy6ISGsmGsed5fj5XGU7WbX4QBf1Z6E7zPhnTuM2FcQwu/gzhOBK4gjhOBK4jjROAK4jgRrLaz4pcAfBLAhfD2F1X1wYvtK5VO49r9+zr3H2PjOy28rJRRsjD+GnfqA4AnHzLqCco8w8/q+t8/xMZlT54N26mJCZLVa5wB0GzatRbWPnft3E0y6xtMwcauGDUdxQWOwvf0sAE8Mmwb6YFxfXbdzI0Xxq7jcRNiRPaTcX6v40k+iHXNYkn7OlaMcQ75OGdIxJcdO2E4hyxW21kRAL6mqofa/y6qHI6zFVltZ0XH+a1gLTbIZ0XkiIjcIyJcN9lmaWfFwqx3VnS2FqtVkK8DuBbAIQATAL6y0oZLOyv2DnhnRWdrsapIuqq+YR2LyDcA/LCb1zWbTRSLnZHPhGGklSps4KWNsPDcrB1Jr1aM2Xp1Y8ahFUnPGI0KFrgJwNkz3NxBjZl+J4+fNNdYNjoz5rOcij62g9PiFRzZr1Q51f7ka6+QLJviYyyk7Sfo/DB/oSX7uIPjbJmPrWU2qhPGDMbAMNyThndAQnuOYgzGWITQyD6QZe9/d4H01d1B2u1GL/BRADz50nGuALpx834bwHsADIvIKQB/DeA9InIIrabVJwB8+hKu0XEuG6vtrPgvl2AtjrPp8Ei640SwoenuoSrK1U6DN6yyAZxPGnP0jLrwhUU7BbpaZWO5abzeqhefL7DBGoYcmZ2b5/mGFcNYDWPcyRAAEkZkuGGk2sfFqivnt21+jjsPnp/kaH82zt+J9Yp9Hfvi7L3fBc4ACMt8fZqGoZywMgCMqHnGSFdvjaFhmgHLJcay2LKMDVFPd3ecNeMK4jgRuII4TgSuII4TwYYa6QAgyyKnWaPFfjpgIy2jrMuJuDGLEECzueJM0Q7qxmiB6WmOkI/tYsP0lrdxczOJs+F3Zpwb3gHAyVNPkiyTYaO4boyCSKf4mtWNKH61xpkGxTmOpDcCY7gigL4UR6nDODs2qg2+3g3DSA/rfL1jhpFeEz6X8qK9Rg34mieTnDWRS3duZzleLPwO4jgRuII4TgSuII4TgSuI40SwoUa6GpH0uhqpyYb9FMTZcIdRzw4AYjSZswKxahiSuTwbwB/90w+QbO8NbKTHU7zGI0+9YK7xpz96lGTnjbmFzTobxc2YUfsOI3KdYdlChQ337UOcUg8AB27m5na9/Wy4F6rcRM+aAVirsaFdN0obrBT4utHxHwDCqpXaztkL1WWN4pordN1fjt9BHCcCVxDHicAVxHEicAVxnAi6qSjcBeBfAYygVUF4t6r+o4gMAvgOgD1oVRV+TFXtae9tFECITmu50WTjS40UbyvwWavahlutyoafsUuI8fWwbz+PE9t/kLuXZ4b50tWUDb/D736Hucb9+99CsvEzZ0h25ixH9qF8bA35BH90/0Mkm3iZR8wNjnCaPQBs38kN4RZKPN4sbPJ5xwI2nkWMN9H4BBaMLvdN4xgAkBI26GOGR6Za7vysrGckvQHgC6p6EMCtAD4jIgcB3AXgYVXdB+Dh9t+Oc0XRTeO4CVV9uv17EcBRAGMAbgdwb3uzewF85FIt0nEuF2/KBhGRPQB+B8DjAEZU9ULJ2lm0HsGs17zROG5hnivuHGcz07WCiEgewPcAfE5VO6JN2qqHNB/qljaOy/UZPYwcZxPTlYKISICWcnxLVb/fFk9e6I/V/mkMGHecrU03XixBq83PUVX96pL/egDAHQC+3P55fxf7QrC8a55RQ9E00k+MjAvMTdu9fms1ozmA0azAcm3tNobYZ1PcYXBhntNCakZjiOQKLfwG8lz70bOfuxaODA2SzPLAGGUjeOx/HiPZuOE17OmxuxbmjIYIzRKncRiTF8y0m5rR/TFjdVFM8zWrGesGgIRx4mJ8fpZ7T1d44OH9d7HNuwD8GYDnRORXbdkX0VKM74rInQBOAvhYV0d0nC1EN43jHsXKnUzfv77LcZzNhUfSHScCVxDHiWBjmzaoUlqC1cp/ocx6qyHXaVTmbUMrtOYCxvgpMWs0Jbj6Kp7BF6vwGmWBj50MOe0hZRW3AAgCdiRk49wcIhXjc6k0uKaj1GDjOWk4NpJxbtpwwzVj5hr3DhqpJkleY8nocDlvzGusFK2RCMZ7ZaSkiJUXBHPSAerG3MJmo3ON4QqdGpfjdxDHicAVxHEicAVxnAhcQRwngg1v2tCsdBqTGhhjCWrGDL4iyyZP210L1Yg0G+US6O/rI9mQIZs7a3RwDPnSpRNs9DcadkfAWNwwqo13I6izAVwpcwaBGk0S1Oh4GBhNKQa28zkDQDrNC4oLG/mG/wN9yhHyoQbLJie43qVhOBwq4QpNGwzj3TL8q6Vl74OVemDgdxDHicAVxHEicAVxnAhcQRwngg010mMKpJbZ2rGAo89loxnDzAQb5Oen7BIUK7PS6heQy7JRvVBkA/i1l06QLBnj1w71cYfChDEvDwDCpJFKXj1PMjWM/KqVX57hlPy48NubyhkNH8RuiLBY4JR+VTbSYaXQxzMkS6d5jWEPOwikOMfHLdtGdWG58Q0gnTQyMYqdn7NY02cUOs6acQVxnAhcQRwnAlcQx4lgLZ0VvwTgkwAuWM9fVNUHo/YVgyAbdkZTa2WOmqLE6cqL57ijX21hhSi1YaaLUX8exI3uiGXepxjp94UK11c3Cmzs9vWyYQoAkjVq8RdneJ81Pk49yfXe8STXs4sR7e/NGPMf67YBXJ/itHoFG+lqpOTPhvwe1ow5ijEjWp8O2cCPG+cMAIkGX8d4jR0g8WrnumPanZHejRfrQmfFp0WkB8AvReRCT8uvqeo/dHUkx9mCdFOTPgFgov17UUQudFZ0nCuetXRWBIDPisgREblHRLiPDTo7K5YK3lnR2VqspbPi1wFcC+AQWneYr1ivW9pZMd/rnRWdrUVXkXSrs6KqTi75/28A+OFFd9QEYsuCs0GCI+m94LrnoMLR1WqJU8EBIB5jvQ+SbFymAjb80sZ2PSlOES812blQmOdZfcV5eyJEIsavv26Uo8qZPF+LQoGj/fNnOKtgbpaN7IEcG8ADwjIAyC4YWQ7GTMHQmIVYXp4yAaBujJRU4/2PG3XziRWy061ov3FpkdTO91pW7GS1bF8X22ClzooX2o62+SiA57s6ouNsIdbSWfETInIILdfvCQCfviQrdJzLyFo6K0bGPBznSsAj6Y4TwcamuyOGnHYanaEx1y+RZG/X7iFeal/uFfM4QYIN46TRRTxhDKxfWGCjr1nj6HqQYMO2Z4Cj2aFRXw0AiwuGgyHPRnoixw6CsMIW6+lxNtKnZ9mxMTy8m2SBER0HgGST5ZUqn0+xzBkAlV6+tknj+sTT/L7US+wIaNTt62hlJKiRYLE84N5d2zi/gzhOJK4gjhOBK4jjROAK4jgRbLiRntJOAzxmdD/XGhtuuYANvH3X7jOP0zQisePjp0lWKnDk+9grx3h/Rl14Is7G8+g2zuHsMYxsANi2nUer1ZJs+JeEjV1N83YNI50/leXtakaWQV3sVHKNG6n6Rnc7aXBkv17kKH4s4NcGxrp7jcyFecNRAgDxPl57o2o0I5xflmpvHNfC7yCOE4EriONE4AriOBG4gjhOBK4gjhPBxs4oBKDxi8f800b6wZ69O0nWv2ObeYx3LrLH49H/fYxkrx1nj1WtZgyhr3KzgWrIqQ+v13i7dMZO45DcdSTLp4zzMboEJvvZO9U7xJ6fwSE+9sIie5ymZ20P0eCeHSQLA15PptpLsqDGXqJKhWVVNeYJZvlDsWCMdwCAsuGMSuc5VWm5E1SsmQ0GfgdxnAhcQRwnAlcQx4mgm5LbtIg8ISLPisgLIvI3bfk1IvK4iBwTke+IrBCOdZwtTDdGehXA+1S11G7e8KiI/AjA59FqHHefiPwzgDvR6nSyIqqKeqPT2LLqNNIpNi6DGG+X7eGGBgCwQ1nvt/fdRrLx1zn9pDfPKS1Tr79Gsvl5rrUI8mw8V0N7tEDd8E5UjHmEGuO3qFTiFJmEkbJz4/XXkKxvgJ0dg/08tgEApit8nB1Xs+FeOM0Oi8lZHuVQqLOhXTMcMlpnAzrM2N/lqQR/VqZnuT7l9MlTHX9XalxzYnHRO4i2uFBFFLT/KYD3AfiPtvxeAB/p6oiOs4XoygYRkXi7YcMUgIcAHAcwp78ZrXoKK3RbXNo4rlBiF6PjbGa6UhBVbarqIQA7AbwdwPXdHmBp47jevN3I2XE2K2/Ki6WqcwB+BuCdAPpF3pjxtRMAP9A7zhanm/EH2wDUVXVORDIAPgjg79FSlD8BcB+AOwDcf9GjqSBe7zxkLMFLiBk1CzWjaD9pl1pAjOHyO4c4Sj02OEKywjyPWehL8P5mZqdJVrIaCxj1EwAQGl9NWmFDu2p0MkwbcwL7R7gZw/49fKOvG0a/Vm2DNd/LDot8H1/06gzXrFTBEfIzU5MkKxmG++AY18oEeTbmASAEX/OUUQ/UM9DZOjpuNOyw6MaLNQrgXhGJo3XH+a6q/lBEXgRwn4j8LYBn0Oq+6DhXFN00jjuCVkf35fJX0bJHHOeKxSPpjhOBK4jjRCCq3faYW4eDiZwDcBLAMAAOtW5N/Fw2Jxc7l6tV1a6XWMKGKsgbBxV5SlUPb/iBLwF+LpuT9ToXf8RynAhcQRwngsulIHdfpuNeCvxcNifrci6XxQZxnK2CP2I5TgSuII4TwYYriIjcJiIvtUt179ro468FEblHRKZE5PklskEReUhEXmn/HIjax2ZBRHaJyM9E5MV2KfVftOVb7nwuZVn4hipIO+HxnwB8CMBBtCblHtzINayRbwJYXrt7F4CHVXUfgIfbf28FGgC+oKoHAdwK4DPt92Irns+FsvBbABwCcJuI3IpW1vnXVPU6ALNolYW/KTb6DvJ2AMdU9VVVraGVKn/7Bq9h1ajqIwCWFzzfjlbJMbCFSo9VdUJVn27/XgRwFK2q0C13PpeyLHyjFWQMwPiSv1cs1d1CjKjqRPv3swC4yGSTIyJ70MrYfhxb9HzWUhYehRvp64i2fOZbym8uInkA3wPwOVXtmHqzlc5nLWXhUWy0gpwGsGvJ31dCqe6kiIwCQPsnz2PepLTbOH0PwLdU9ftt8ZY9H2D9y8I3WkGeBLCv7V1IAvg4gAc2eA3rzQNolRwD3ZYebwJERNCqAj2qql9d8l9b7nxEZJuI9Ld/v1AWfhS/KQsHVnsuqrqh/wB8GMDLaD0j/uVGH3+Na/82gAkAdbSeae8EMISWt+cVAD8FMHi519nlubwbrcenIwB+1f734a14PgBuRqvs+wiA5wH8VVu+F8ATAI4B+HcAqTe7b081cZwI3Eh3nAhcQRwnAlcQx4nAFcRxInAFcZwIXEEcJwJXEMeJ4P8BDOlRG0/6AGEAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 216x216 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "import matplotlib.pyplot as plt\n",
    "import imgaug as ia\n",
    "import imgaug.augmenters as iaa\n",
    "\n",
    "# 读取数据\n",
    "reader = paddle.batch(\n",
    "    paddle.dataset.cifar.train10(),\n",
    "    batch_size=8) # 数据集读取器\n",
    "data = next(reader()) # 读取数据\n",
    "index = 4 # 批次索引\n",
    "\n",
    "# 读取图像\n",
    "image = np.array([x[0] for x in data]).astype(np.float32) # 读取图像数据，数据类型为float32\n",
    "image = image * 255 # 从[0,1]转换到[0,255]\n",
    "image = image[index].reshape((3, 32, 32)).transpose((1, 2, 0)).astype(np.uint8) # 数据格式从CHW转换为HWC，数据类型转换为uint8\n",
    "print('image shape:', image.shape)\n",
    "\n",
    "# 图像增强\n",
    "# sometimes = lambda aug: iaa.Sometimes(0.5, aug) # 随机进行图像增强\n",
    "# seq = iaa.Sequential([\n",
    "#     sometimes(iaa.CropAndPad(px=(-4, 4))),      # 随机裁剪填充像素\n",
    "#     iaa.Fliplr(0.5)])                           # 随机进行水平翻转\n",
    "# image = seq(image=image)\n",
    "\n",
    "# 读取标签\n",
    "label = np.array([x[1] for x in data]).astype(np.int64) # 读取标签数据，数据类型为int64\n",
    "vlist = [\"airplane\", \"automobile\", \"bird\", \"cat\", \"deer\", \"dog\", \"frog\", \"horse\", \"ship\", \"truck\"] # 标签名称列表\n",
    "print('label value:', vlist[label[index]])\n",
    "\n",
    "# 显示图像\n",
    "image = Image.fromarray(image)   # 转换图像格式\n",
    "image.save('./work/out/img.png') # 保存读取图像\n",
    "plt.figure(figsize=(3, 3))       # 设置显示大小\n",
    "plt.imshow(image)                # 设置显示图像\n",
    "plt.show()                       # 显示图像文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_data: image shape (128, 3, 32, 32), label shape:(128, 1)\n",
      "valid_data: image shape (128, 3, 32, 32), label shape:(128, 1)\n"
     ]
    }
   ],
   "source": [
    "import paddle\n",
    "import numpy as np\n",
    "import imgaug as ia\n",
    "import imgaug.augmenters as iaa\n",
    "\n",
    "# 训练数据增强\n",
    "def train_augment(images):\n",
    "    # 转换格式\n",
    "    images = images * 255 # 从[0,1]转换到[0,255]\n",
    "    images = images.transpose((0, 2, 3, 1)).astype(np.uint8) # 数据格式从BCHW转换为BHWC，数据类型转换为uint8\n",
    "    \n",
    "    # 增强图像\n",
    "    sometimes = lambda aug: iaa.Sometimes(0.5, aug) # 随机进行图像增强\n",
    "    seq = iaa.Sequential([\n",
    "        sometimes(iaa.CropAndPad(px=(-4, 4))),      # 随机裁剪填充像素\n",
    "        iaa.Fliplr(0.5)])                           # 随机进行水平翻转\n",
    "    images = seq(images=images)\n",
    "    \n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    images = (images/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    images = images.transpose((0, 3, 1, 2)).astype(np.float32) # 数据格式从BHWC转换为BCHW，数据类型转换为float32\n",
    "    \n",
    "    return images\n",
    "\n",
    "# 验证数据增强\n",
    "def valid_augment(images):\n",
    "    # 转换格式\n",
    "    images = images * 255 # 从[0,1]转换到[0,255]\n",
    "    images = images.transpose((0, 2, 3, 1)).astype(np.uint8) # 数据格式从BCHW转换为BHWC，数据类型转换为uint8\n",
    "    \n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    images = (images/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    images = images.transpose((0, 3, 1, 2)).astype(np.float32) # 数据格式从BHWC转换为BCHW，数据类型转换为float32\n",
    "    \n",
    "    return images\n",
    "\n",
    "# 读取训练数据\n",
    "train_reader = paddle.batch(\n",
    "    paddle.reader.shuffle(paddle.dataset.cifar.train10(), buf_size=50000),\n",
    "    batch_size=128) # 构造数据读取器\n",
    "train_data = next(train_reader()) # 读取训练数据\n",
    "\n",
    "train_image = np.array([x[0] for x in train_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取训练图像\n",
    "train_image = train_augment(train_image)                                                       # 训练图像增强\n",
    "train_label = np.array([x[1] for x in train_data]).reshape((-1, 1)).astype(np.int64)           # 读取训练标签\n",
    "print('train_data: image shape {}, label shape:{}'.format(train_image.shape, train_label.shape))\n",
    "\n",
    "# 读取验证数据\n",
    "valid_reader = paddle.batch(\n",
    "    paddle.dataset.cifar.test10(),\n",
    "    batch_size=128) # 构造数据读取器\n",
    "valid_data = next(valid_reader()) # 读取验证数据\n",
    "\n",
    "valid_image = np.array([x[0] for x in valid_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取验证图像\n",
    "valid_image = valid_augment(valid_image)                                                       # 验证图像增强\n",
    "valid_label = np.array([x[1] for x in valid_data]).reshape((-1, 1)).astype(np.int64)           # 读取验证标签\n",
    "print('valid_data: image shape {}, label shape:{}'.format(valid_image.shape, valid_label.shape))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 模型设计"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear, BatchNorm\n",
    "import math\n",
    "\n",
    "# 模组结构：输入维度，输出维度，滑动步长，基础长度, 队列长度\n",
    "group_arch = [(3, 256, 1, 2, 1), (256, 256, 2, 2, 1), (256, 256, 2, 2, 1)]\n",
    "group_dim  = 256 # 模组输出维度\n",
    "class_dim  = 10  # 类别数量维度\n",
    "\n",
    "# 卷积单元\n",
    "class ConvUnit(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, filter_size=3, stride=1, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化卷积单元，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim      - 输入维度\n",
    "            out_dim     - 输出维度\n",
    "            filter_size - 卷积大小\n",
    "            stride      - 滑动步长\n",
    "            act         - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ConvUnit, self).__init__()\n",
    "        \n",
    "        # 添加卷积\n",
    "        self.conv = Conv2D(\n",
    "            num_channels=in_dim,\n",
    "            num_filters=out_dim,\n",
    "            filter_size=filter_size,\n",
    "            stride=stride,\n",
    "            padding=(filter_size-1)//2,                       # 输出特征图大小不变\n",
    "            param_attr=fluid.initializer.MSRA(uniform=False), # 使用MARA 初始权重\n",
    "            bias_attr=False,                                  # 卷积输出没有偏置项\n",
    "            act=None)\n",
    "        \n",
    "        # 添加正则\n",
    "        self.norm = BatchNorm(\n",
    "            num_channels=out_dim,\n",
    "            param_attr=fluid.initializer.Constant(1.0), # 使用常量初始化权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),  # 使用常量初始化偏置\n",
    "            act=act)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征进行卷积和正则\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 进行卷积\n",
    "        x = self.conv(x)\n",
    "        \n",
    "        # 进行正则\n",
    "        x = self.norm(x)\n",
    "        \n",
    "        return x\n",
    "\n",
    "# 投影单元\n",
    "class ProjUnit(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, filter_size=1, stride=1, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化投影单元，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim      - 输入维度\n",
    "            out_dim     - 输出维度\n",
    "            filter_size - 卷积大小\n",
    "            stride      - 滑动步长\n",
    "            act         - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ProjUnit, self).__init__()\n",
    "        \n",
    "        # 添加池化\n",
    "        self.pool = Pool2D(\n",
    "            pool_size=filter_size,\n",
    "            pool_stride=stride,\n",
    "            pool_padding=0,\n",
    "            pool_type='avg')\n",
    "        \n",
    "        # 添加卷积\n",
    "        self.conv = Conv2D(\n",
    "            num_channels=in_dim,\n",
    "            num_filters=out_dim,\n",
    "            filter_size=1,\n",
    "            stride=1,\n",
    "            padding=0,\n",
    "            param_attr=fluid.initializer.MSRA(uniform=False), # 使用MARA 初始权重\n",
    "            bias_attr=False,                                  # 卷积输出没有偏置项\n",
    "            act=None)\n",
    "        \n",
    "        # 添加正则\n",
    "        self.norm = BatchNorm(\n",
    "            num_channels=out_dim,\n",
    "            param_attr=fluid.initializer.Constant(1.0), # 使用常量初始化权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),  # 使用常量初始化偏置\n",
    "            act=act)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征进行池化卷积和正则\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 进行池化\n",
    "        x = self.pool(x)\n",
    "        \n",
    "        # 进行卷积\n",
    "        x = self.conv(x)\n",
    "        \n",
    "        # 进行正则\n",
    "        x = self.norm(x)\n",
    "        \n",
    "        return x\n",
    "\n",
    "# 队列结构\n",
    "class SSRQueue(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, queues=2, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化队列结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长，1保持不变，2下采样\n",
    "            queues  - 队列长度，分割尺度为2^(n-1)\n",
    "            act     - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRQueue, self).__init__()\n",
    "        \n",
    "        # 添加队列变量\n",
    "        self.queues = queues # 队列长度\n",
    "        self.split_list = [] # 分割列表\n",
    "        \n",
    "        # 添加队列列表\n",
    "        self.queue_list = [] # 队列列表\n",
    "        for i in range(queues):\n",
    "            # 添加队列项目\n",
    "            queue_item = self.add_sublayer( # 构造队列项目\n",
    "                'queue_' + str(i),\n",
    "                ConvUnit(\n",
    "                    in_dim=(in_dim if i==0 else out_dim), # 每组队列项目除第一个外，in_dim=out_dim\n",
    "                    out_dim=out_dim,\n",
    "                    filter_size=3,\n",
    "                    stride=(stride if i==0 else 1), # 每组队列项目除第一块外，stride=1\n",
    "                    act=act))\n",
    "            self.queue_list.append(queue_item) # 添加队列项目\n",
    "            \n",
    "            # 计算输出维度\n",
    "            if i < (queues-1): # 如果不是最后一项\n",
    "                out_dim = out_dim//2 # 输出维度减半\n",
    "                self.split_list.append(out_dim) # 添加分割列表\n",
    "            \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 提取特征\n",
    "        x_list = [] # 队列输出列表\n",
    "        for i, queue_item in enumerate(self.queue_list):\n",
    "            if i < (self.queues-1): # 如果不是最后一项\n",
    "                x = queue_item(x) # 提取队列特征\n",
    "                x_item, x = fluid.layers.split(input=x, num_or_sections=[-1, self.split_list[i]], dim=1)\n",
    "                x_list.append(x_item) # 添加输出列表\n",
    "            else: # 否则不对特征分割\n",
    "                x = queue_item(x) # 提取队列特征\n",
    "                x_list.append(x) # 添加输出列表\n",
    "        \n",
    "        # 联结特征\n",
    "        x = fluid.layers.concat(input=x_list, axis=1) # 队列输出列表按通道维进行特征联结\n",
    "        \n",
    "        return x\n",
    "    \n",
    "# 基础结构\n",
    "class SSRBasic(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, queues=1, is_pass=True):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化基础结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长\n",
    "            queues  - 队列长度\n",
    "            is_pass - 是否直连\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRBasic, self).__init__()\n",
    "        \n",
    "        # 是否直连标识\n",
    "        self.is_pass = is_pass\n",
    "        \n",
    "        # 添加投影路径\n",
    "        self.proj = ProjUnit(in_dim=in_dim, out_dim=out_dim, filter_size=stride, stride=stride, act=None)\n",
    "        \n",
    "        # 添加卷积路径\n",
    "        if queues==1:\n",
    "            self.conv = ConvUnit(in_dim=in_dim, out_dim=out_dim, filter_size=3, stride=stride, act='relu')\n",
    "        else:\n",
    "            self.conv = SSRQueue(in_dim=in_dim, out_dim=out_dim, stride=stride, queues=queues, act='relu')\n",
    "        \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "            y - 输出特征\n",
    "        \"\"\"\n",
    "        # 直连路径\n",
    "        if self.is_pass: # 是否直连\n",
    "            x_pass = x\n",
    "        else:            # 否则投影\n",
    "            x_pass = self.proj(x)\n",
    "        \n",
    "        # 卷积路径\n",
    "        x_conv = self.conv(x)\n",
    "        \n",
    "        # 输出特征\n",
    "        x = fluid.layers.elementwise_add(x=x_pass, y=x_conv, act=None) # 直连路径与卷积路径进行特征相加\n",
    "        y = x\n",
    "        \n",
    "        return x, y\n",
    "    \n",
    "# 模块结构\n",
    "class SSRBlock(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, basics=1, queues=1):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化模块结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长\n",
    "            basics  - 基础长度\n",
    "            queues  - 队列长度\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRBlock, self).__init__()\n",
    "        \n",
    "        # 添加模块列表\n",
    "        self.block_list = [] # 模块列表\n",
    "        for i in range(basics):\n",
    "            block_item = self.add_sublayer( # 构造模块项目\n",
    "                'block_' + str(i),\n",
    "                SSRBasic(\n",
    "                    in_dim=(in_dim if i==0 else out_dim), # 每组模块项目除第一块外，输入维度=输出维度\n",
    "                    out_dim=out_dim,\n",
    "                    stride=(stride if i==0 else 1), # 每组模块项目除第一块外，stride=1\n",
    "                    queues=queues,\n",
    "                    is_pass=(False if i==0 else True))) # 每组模块项目除第一块外，is_pass=True\n",
    "            self.block_list.append(block_item) # 添加模块项目\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x      - 输入特征\n",
    "        输出:\n",
    "            x      - 输出特征\n",
    "            y_list - 输出特征列表\n",
    "        \"\"\"\n",
    "        y_list = [] # 模块输出列表\n",
    "        for block_item in self.block_list:\n",
    "            x, y_item = block_item(x) # 提取模块特征\n",
    "            y_list.append(y_item) # 添加输出列表\n",
    "            \n",
    "        return x, y_list\n",
    "\n",
    "# 模组结构\n",
    "class SSRGroup(fluid.dygraph.Layer):\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化模组结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRGroup, self).__init__()\n",
    "        \n",
    "        # 添加模组列表\n",
    "        self.group_list = [] # 模组列表\n",
    "        for i, block_arch in enumerate(group_arch):\n",
    "            group_item = self.add_sublayer( # 构造模组项目\n",
    "                'group_' + str(i),\n",
    "                SSRBlock(\n",
    "                    in_dim=block_arch[0],\n",
    "                    out_dim=block_arch[1],\n",
    "                    stride=block_arch[2],\n",
    "                    basics=block_arch[3],\n",
    "                    queues=block_arch[4]))\n",
    "            self.group_list.append(group_item) # 添加模组项目\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x      - 输入特征\n",
    "        输出:\n",
    "            x      - 输出特征\n",
    "            y_list - 输出特征列表\n",
    "        \"\"\"\n",
    "        y_list = [] # 模组输出列表\n",
    "        for group_item in self.group_list:\n",
    "            x, y_item = group_item(x) # 提取模组特征\n",
    "            y_list.append(y_item) # 添加输出列表\n",
    "            \n",
    "        return x, y_list\n",
    "        \n",
    "# 分割网络\n",
    "class SSRNet(fluid.dygraph.Layer):\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化分割网络，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(SSRNet, self).__init__()\n",
    "        \n",
    "        # 添加模组结构\n",
    "        self.backbone = SSRGroup() # 输出：N*C*H*W\n",
    "        \n",
    "        # 添加全连接层\n",
    "        self.pool = Pool2D(global_pooling=True, pool_type='avg') # 输出：N*C*1*1\n",
    "        \n",
    "        stdv = 1.0/(math.sqrt(group_dim)*1.0)                    # 设置均匀分布权重方差\n",
    "        self.fc = Linear(                                        # 输出：=N*10\n",
    "            input_dim=group_dim,\n",
    "            output_dim=class_dim,\n",
    "            param_attr=fluid.initializer.Uniform(-stdv, stdv),   # 使用均匀分布初始权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),           # 使用常量数值初始偏置\n",
    "            act='softmax')\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入图像进行分类\n",
    "        输入:\n",
    "            x - 输入图像\n",
    "        输出:\n",
    "            x - 预测结果\n",
    "        \"\"\"\n",
    "        # 提取特征\n",
    "        x, y_list = self.backbone(x)\n",
    "        \n",
    "        # 进行预测\n",
    "        x = self.pool(x)\n",
    "        x = fluid.layers.reshape(x, [x.shape[0], -1])\n",
    "        x = self.fc(x)\n",
    "        \n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tatol param: 3299338\n",
      "infer shape: [1, 10]\n"
     ]
    }
   ],
   "source": [
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.dygraph.base import to_variable\n",
    "import numpy as np\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    # 输入数据\n",
    "    x = np.random.randn(1, 3, 32, 32).astype(np.float32)\n",
    "    x = to_variable(x)\n",
    "    \n",
    "    # 进行预测\n",
    "    backbone = SSRNet() # 设置网络\n",
    "    \n",
    "    infer = backbone(x) # 进行预测\n",
    "    \n",
    "    # 显示结果\n",
    "    parameters = 0\n",
    "    for p in backbone.parameters():\n",
    "        parameters += np.prod(p.shape) # 统计参数\n",
    "    \n",
    "    print('tatol param:', parameters)\n",
    "    print('infer shape:', infer.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "complete - train time: 9419s, best epoch: 193, best loss: 0.281373, best accuracy: 93.25%\r"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "from paddle.utils.plot import Ploter\n",
    "import numpy as np\n",
    "import time\n",
    "import math\n",
    "import os\n",
    "\n",
    "epoch_num = 300   # 训练周期，取值一般为[1,300]\n",
    "train_batch = 128 # 训练批次，取值一般为[1,256]\n",
    "valid_batch = 128 # 验证批次，取值一般为[1,256]\n",
    "displays = 100    # 显示迭代\n",
    "\n",
    "start_lr = 0.00001                         # 开始学习率，取值一般为[1e-8,5e-1]\n",
    "based_lr = 0.1                             # 基础学习率，取值一般为[1e-8,5e-1]\n",
    "epoch_iters = math.ceil(50000/train_batch) # 每轮迭代数\n",
    "warmup_iter = 10 * epoch_iters             # 预热迭代数，取值一般为[1,10]\n",
    "\n",
    "momentum = 0.9     # 优化器动量\n",
    "l2_decay = 0.00005 # 正则化系数，取值一般为[1e-5,5e-4]\n",
    "epsilon = 0.05     # 标签平滑率，取值一般为[1e-2,1e-1]\n",
    "\n",
    "checkpoint = True                    # 断点标识\n",
    "model_path = './work/out/ssrnet'     # 模型路径\n",
    "result_txt = './work/out/result.txt' # 结果文件\n",
    "class_num  = 10                      # 类别数量\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    # 准备数据\n",
    "    train_reader = paddle.batch(\n",
    "        reader=paddle.reader.shuffle(reader=paddle.dataset.cifar.train10(), buf_size=50000),\n",
    "        batch_size=train_batch)\n",
    "    \n",
    "    valid_reader = paddle.batch(\n",
    "        reader=paddle.dataset.cifar.test10(),\n",
    "        batch_size=valid_batch)\n",
    "    \n",
    "    # 声明模型\n",
    "    model = SSRNet()\n",
    "    \n",
    "    # 优化算法\n",
    "    consine_lr = fluid.layers.cosine_decay(based_lr, epoch_iters, epoch_num) # 余弦衰减策略\n",
    "    decayed_lr = fluid.layers.linear_lr_warmup(consine_lr, warmup_iter, start_lr, based_lr) # 线性预热策略\n",
    "    \n",
    "    optimizer = fluid.optimizer.Momentum(\n",
    "        learning_rate=decayed_lr,                           # 衰减学习策略\n",
    "        momentum=momentum,                                  # 优化动量系数\n",
    "        regularization=fluid.regularizer.L2Decay(l2_decay), # 正则衰减系数\n",
    "        parameter_list=model.parameters())\n",
    "    \n",
    "    # 加载断点\n",
    "    if checkpoint: # 是否加载断点文件\n",
    "        model_dict, optimizer_dict = fluid.load_dygraph(model_path) # 加载断点参数\n",
    "        model.set_dict(model_dict)                                  # 设置权重参数\n",
    "        optimizer.set_dict(optimizer_dict)                          # 设置优化参数\n",
    "    else:          # 否则删除结果文件\n",
    "        if os.path.exists(result_txt): # 如果存在结果文件\n",
    "            os.remove(result_txt)      # 那么删除结果文件\n",
    "    \n",
    "    # 初始训练\n",
    "    avg_train_loss = 0 # 平均训练损失\n",
    "    avg_valid_loss = 0 # 平均验证损失\n",
    "    avg_valid_accu = 0 # 平均验证精度\n",
    "    \n",
    "    iterator = 1                                # 迭代次数\n",
    "    train_prompt = \"Train loss\"                 # 训练标签\n",
    "    valid_prompt = \"Valid loss\"                 # 验证标签\n",
    "    ploter = Ploter(train_prompt, valid_prompt) # 训练图像\n",
    "    \n",
    "    best_epoch = 0           # 最好周期\n",
    "    best_accu = 0            # 最好精度\n",
    "    best_loss = 100.0        # 最好损失\n",
    "    train_time = time.time() # 训练时间\n",
    "    \n",
    "    # 开始训练\n",
    "    for epoch_id in range(epoch_num):\n",
    "        # 训练模型\n",
    "        model.train() # 设置训练\n",
    "        for batch_id, train_data in enumerate(train_reader()):\n",
    "            # 读取数据\n",
    "            image_data = np.array([x[0] for x in train_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取图像数据\n",
    "            image_data = train_augment(image_data)                                                        # 使用数据增强\n",
    "            image = fluid.dygraph.to_variable(image_data)                                                 # 转换数据类型\n",
    "\n",
    "            label_data = np.array([x[1] for x in train_data]).astype(np.int64)                        # 读取标签数据\n",
    "            label = fluid.dygraph.to_variable(label_data)                                             # 转换数据类型\n",
    "            label = fluid.layers.label_smooth(label=fluid.one_hot(label, class_num), epsilon=epsilon) # 使用标签平滑\n",
    "            label.stop_gradient = True                                                                # 停止梯度传播\n",
    "\n",
    "            # 前向传播\n",
    "            infer = model(image)\n",
    "            \n",
    "            # 计算损失\n",
    "            loss = fluid.layers.cross_entropy(infer, label, soft_label=True)\n",
    "            train_loss = fluid.layers.mean(loss)\n",
    "            \n",
    "            # 反向传播\n",
    "            train_loss.backward()\n",
    "            optimizer.minimize(train_loss)\n",
    "            model.clear_gradients()\n",
    "            \n",
    "            # 显示结果\n",
    "            if iterator % displays == 0:\n",
    "                # 显示图像\n",
    "                avg_train_loss = train_loss.numpy()[0]                # 设置训练损失\n",
    "                ploter.append(train_prompt, iterator, avg_train_loss) # 添加训练图像\n",
    "                ploter.plot()                                         # 显示训练图像\n",
    "                \n",
    "                # 打印结果\n",
    "                print(\"iteration: {:6d}, epoch: {:3d}, train loss: {:.6f}, valid loss: {:.6f}, valid accuracy: {:.2%}\".format(\n",
    "                    iterator, epoch_id+1, avg_train_loss, avg_valid_loss, avg_valid_accu))\n",
    "                \n",
    "                # 写入文件\n",
    "                with open(result_txt, 'a') as file:\n",
    "                    file.write(\"iteration: {:6d}, epoch: {:3d}, train loss: {:.6f}, valid loss: {:.6f}, valid accuracy: {:.2%}\\n\".format(\n",
    "                        iterator, epoch_id+1, avg_train_loss, avg_valid_loss, avg_valid_accu))\n",
    "            \n",
    "            # 增加迭代\n",
    "            iterator += 1\n",
    "            \n",
    "        # 验证模型\n",
    "        valid_loss_list = [] # 验证损失列表\n",
    "        valid_accu_list = [] # 验证精度列表\n",
    "        \n",
    "        model.eval()   # 设置验证\n",
    "        for batch_id, valid_data in enumerate(valid_reader()):\n",
    "            # 读取数据\n",
    "            image_data = np.array([x[0] for x in valid_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取图像数据\n",
    "            image_data = valid_augment(image_data)                                                        # 使用图像增强\n",
    "            image = fluid.dygraph.to_variable(image_data)                                                 # 转换数据类型\n",
    "            \n",
    "            label_data = np.array([x[1] for x in valid_data]).reshape((-1, 1)).astype(np.int64) # 读取标签数据\n",
    "            label = fluid.dygraph.to_variable(label_data)                                       # 转换数据类型\n",
    "            label.stop_gradient = True                                                          # 停止梯度传播\n",
    "            \n",
    "            # 前向传播\n",
    "            infer = model(image)\n",
    "            \n",
    "            # 计算精度\n",
    "            valid_accu = fluid.layers.accuracy(infer,label)\n",
    "            \n",
    "            valid_accu_list.append(valid_accu.numpy())\n",
    "            \n",
    "            # 计算损失\n",
    "            loss = fluid.layers.cross_entropy(infer, label)\n",
    "            valid_loss = fluid.layers.mean(loss)\n",
    "            \n",
    "            valid_loss_list.append(valid_loss.numpy())\n",
    "        \n",
    "        # 设置结果\n",
    "        avg_valid_accu = np.mean(valid_accu_list)             # 设置验证精度\n",
    "        \n",
    "        avg_valid_loss = np.mean(valid_loss_list)             # 设置验证损失\n",
    "        ploter.append(valid_prompt, iterator, avg_valid_loss) # 添加训练图像\n",
    "        \n",
    "        # 保存模型\n",
    "        fluid.save_dygraph(model.state_dict(), model_path)     # 保存权重参数\n",
    "        fluid.save_dygraph(optimizer.state_dict(), model_path) # 保存优化参数\n",
    "        \n",
    "        if avg_valid_loss < best_loss:\n",
    "            fluid.save_dygraph(model.state_dict(), model_path + '-best') # 保存权重\n",
    "            \n",
    "            best_epoch = epoch_id + 1                                    # 更新迭代\n",
    "            best_accu = avg_valid_accu                                   # 更新精度\n",
    "            best_loss = avg_valid_loss                                   # 更新损失\n",
    "    \n",
    "    # 显示结果\n",
    "    train_time = time.time() - train_time # 设置训练时间\n",
    "    print('complete - train time: {:.0f}s, best epoch: {:3d}, best loss: {:.6f}, best accuracy: {:.2%}'.format(\n",
    "        train_time, best_epoch, best_loss, best_accu))\n",
    "    \n",
    "    # 写入文件\n",
    "    with open(result_txt, 'a') as file:\n",
    "        file.write('complete - train time: {:.0f}s, best epoch: {:3d}, best loss: {:.6f}, best accuracy: {:.2%}\\n'.format(\n",
    "            train_time, best_epoch, best_loss, best_accu))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 模型预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "infer time: 0.004992s, infer value: horse\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 216x216 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle.fluid as fluid\n",
    "from PIL import Image\n",
    "import numpy as np\n",
    "import time\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "image_path = './work/out/img.png' # 图片路径\n",
    "model_path = './work/out/ssrnet-best' # 模型路径\n",
    "\n",
    "# 加载图像\n",
    "def load_image(image_path):\n",
    "    \"\"\"\n",
    "    功能:\n",
    "        读取图像并转换到输入格式\n",
    "    输入:\n",
    "        image_path - 输入图像路径\n",
    "    输出:\n",
    "        image - 输出图像\n",
    "    \"\"\"\n",
    "    # 读取图像\n",
    "    image = Image.open(image_path) # 打开图像文件\n",
    "    \n",
    "    # 转换格式\n",
    "    image = image.resize((32, 32), Image.ANTIALIAS) # 调整图像大小\n",
    "    image = np.array(image, dtype=np.float32) # 转换数据格式，数据类型转换为float32\n",
    "\n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    image = (image/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    image = image.transpose((2, 0, 1)).astype(np.float32) # 数据格式从HWC转换为CHW，数据类型转换为float32\n",
    "    \n",
    "    # 增加维度\n",
    "    image = np.expand_dims(image, axis=0) # 增加数据维度\n",
    "    \n",
    "    return image\n",
    "\n",
    "# 预测图像\n",
    "with fluid.dygraph.guard():\n",
    "    # 读取图像\n",
    "    image = load_image(image_path)\n",
    "    image = fluid.dygraph.to_variable(image)\n",
    "    \n",
    "    # 加载模型\n",
    "    model = SSRNet()                               # 加载模型\n",
    "    model_dict, _ = fluid.load_dygraph(model_path) # 加载权重\n",
    "    model.set_dict(model_dict)                     # 设置权重\n",
    "    model.eval()                                   # 设置验证\n",
    "    \n",
    "    # 前向传播\n",
    "    infer_time = time.time()              # 推断开始时间\n",
    "    infer = model(image)\n",
    "    infer_time = time.time() - infer_time # 推断结束时间\n",
    "    \n",
    "    # 显示结果\n",
    "    vlist = [\"airplane\", \"automobile\", \"bird\", \"cat\", \"deer\", \"dog\", \"frog\", \"horse\", \"ship\", \"truck\"] # 标签名称列表\n",
    "    print('infer time: {:f}s, infer value: {}'.format(infer_time, vlist[np.argmax(infer.numpy())]) )\n",
    "    \n",
    "    image = Image.open(image_path) # 打开图像文件\n",
    "    plt.figure(figsize=(3, 3))     # 设置显示大小\n",
    "    plt.imshow(image)              # 设置显示图像\n",
    "    plt.show()                     # 显示图像文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 1.8.4 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
